今天我想要實現一個功能:當使用者搜尋特定節點後,自動將該節點置中到畫面中央。聽起來很簡單,對吧?就像 Google Maps 那樣,點擊一個地點就能自動移動到那裡。
但這個看似簡單的功能,卻讓我陷入了座標系統的地獄...
我發現了幾個不同的座標系統在同時運作:
zoomInner 的 translate 和 scale
#graph 容器的尺寸和位置// 我嘗試的各種計算方式
const nodeX = bbox.x + bbox.width / 2;  // SVG 座標
const nodeY = bbox.y + bbox.height / 2;
// 嘗試轉換到螢幕座標
const scaledX = nodeX * scale;
const scaledY = nodeY * scale;
// 嘗試計算移動距離
const moveX = containerCenterX - scaledX;
const moveY = containerCenterY - scaledY;
但這些計算總是會有微妙的偏移,讓我懷疑人生。
function centerNode(nodeId) {
  const bbox = node.getBBox();
  const moveX = containerCenterX - nodeX;
  const moveY = containerCenterY - nodeY;
  // 結果:總是偏右邊
}
function centerNode(nodeId) {
  const rect = node.getBoundingClientRect();
  const containerRect = graph.getBoundingClientRect();
  // 結果:座標完全錯亂
}
function centerNode(nodeId) {
  // 先設定縮放為 100%
  state.scalePct = 100;
  applyZoom(100);
  // 然後再置中
  // 結果:還是有偏移
}
最後我決定移除所有縮放功能,固定使用 100% 縮放,但問題依然存在。
這些差異讓座標轉換變得極其複雜。
經過無數次的嘗試,我決定採用一個更實用的方案:讓使用者直接拖曳 SVG 來移動地圖。
// 拖曳 SVG 本身
document.addEventListener('mousedown', function(e) {
  if (e.target.closest('svg')) {
    startDrag(e);
  }
});
// 避免在節點上啟動拖曳
if (e.target.closest('g.node')) {
  return; // 不啟動拖曳
}
這個方案雖然不是自動置中,但至少讓使用者能夠手動將想要的節點移到畫面中央。
我深刻體會到不同座標系統之間的轉換是多麼複雜。每個系統都有自己的原點、單位和方向。
縮放比例會影響所有座標計算,即使設定為 100%,仍然可能有微妙的差異。
不同瀏覽器對 SVG 和 CSS Transform 的處理可能有細微差異。
在 SVG 上處理滑鼠事件需要考慮事件冒泡、座標轉換等問題。
function centerNode(nodeId) {
  const bbox = node.getBBox();
  const moveX = containerCenterX - bbox.x;
  const moveY = containerCenterY - bbox.y;
  zoomInner.style.transform = `translate(${moveX}px, ${moveY}px)`;
}
function centerNode(nodeId) {
  const scale = state.scalePct / 100;
  const scaledX = bbox.x * scale;
  const scaledY = bbox.y * scale;
  // 複雜的計算...
}
// 讓使用者直接拖曳 SVG
document.addEventListener('mousedown', function(e) {
  if (e.target.closest('svg') && !e.target.closest('g.node')) {
    startDrag(e);
  }
});
今天的開發確實是「徒勞無功」的,我沒有實現自動置中功能。但是,這個過程讓我深刻理解了:
有時候,承認某個功能太複雜,改用更簡單的方案,也是一種智慧。今天的「失敗」讓我學會了:
雖然今天沒有做出成果,但這個嘗試又嘗試的過程,本身就是一種收穫。在程式設計的路上,失敗和成功一樣重要,甚至更重要,因為失敗教會我們更多。
明天,我會帶著今天的經驗,繼續前進。也許下次遇到類似的問題時,我會更早地選擇更簡單的解決方案,而不是陷入座標系統的地獄中。
沒有做出成果,也是一種成果。什麼收穫都沒有,也是一種收穫。
Day 24 完成。座標系統的地獄,讓我學會了謙遜,也讓我學會了選擇。